// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package collider

import (
	"io"
	"log"
	"sync"
	"time"
)

// roomTable 是一个线程安全的房间映射表。
type roomTable struct {
	lock            sync.Mutex
	rooms           map[string]*room
	registerTimeout time.Duration
	roomSrvUrl      string
}

// newRoomTable 创建一个新的房间表实例。
func newRoomTable(to time.Duration, rs string) *roomTable {
	log.Printf(">>> 创建新房间表: 超时=%v, 服务器URL=%s", to, rs)
	return &roomTable{rooms: make(map[string]*room), registerTimeout: to, roomSrvUrl: rs}
}

// room 返回由 |id| 指定的房间，如果房间不存在，则创建它。
func (rt *roomTable) room(id string) *room {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	return rt.roomLocked(id)
}

// roomLocked 在不获取锁的情况下获取或创建房间。当调用者已获取锁时使用。
func (rt *roomTable) roomLocked(id string) *room {
	if r, ok := rt.rooms[id]; ok {
		return r
	}
	log.Printf(">>> 房间表中找不到房间 %s，正在创建...", id)
	rt.rooms[id] = newRoom(rt, id, rt.registerTimeout, rt.roomSrvUrl)
	log.Printf(">>> 房间 %s 创建成功", id)

	return rt.rooms[id]
}

// remove 移除客户端。如果房间变空，它也会移除该房间。
func (rt *roomTable) remove(rid string, cid string) {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	log.Printf(">>> 正在从房间表移除: 房间ID=%s, 客户端ID=%s", rid, cid)
	rt.removeLocked(rid, cid)
}

// removeLocked 在不获取锁的情况下移除客户端。当调用者已获取锁时使用。
func (rt *roomTable) removeLocked(rid string, cid string) {
	if r := rt.rooms[rid]; r != nil {
		r.remove(cid)
		if r.empty() {
			delete(rt.rooms, rid)
			log.Printf(">>> 房间 %s 已空并被移除", rid)
		}
	}
}

// send 将消息转发到房间。如果房间不存在，则会创建一个。
func (rt *roomTable) send(rid string, srcID string, msg string) error {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	r := rt.roomLocked(rid)
	return r.send(srcID, msg)
}

// register 将注册请求转发到房间。如果房间不存在，则会创建一个。
func (rt *roomTable) register(rid string, cid string, rwc io.ReadWriteCloser) error {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	log.Printf(">>> 房间表正在处理注册: 房间ID=%s, 客户端ID=%s", rid, cid)
	r := rt.roomLocked(rid)
	return r.register(cid, rwc)
}

// deregister 清除客户端的 WebSocket 注册。
// 我们会在超时后才移除客户端，以便在网络间漫游的用户可以无缝重新连接。
func (rt *roomTable) deregister(rid string, cid string) {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	log.Printf(">>> 房间表正在处理注销: 房间ID=%s, 客户端ID=%s", rid, cid)
	if r := rt.rooms[rid]; r != nil {
		if c := r.clients[cid]; c != nil {
			if c.registered() {
				c.deregister()

				c.setTimer(time.AfterFunc(rt.registerTimeout, func() {
					rt.removeIfUnregistered(rid, c)
				}))

				log.Printf(">>> 客户端 %s 已从房间 %s 注销", c.id, rid)
				return
			}
		}
	}
}

// removeIfUnregistered 如果客户端尚未注册，则将其移除。
func (rt *roomTable) removeIfUnregistered(rid string, c *client) {
	log.Printf(">>> 因超时，正在尝试移除未注册的客户端 %s (房间 %s)", c.id, rid)

	rt.lock.Lock()
	defer rt.lock.Unlock()

	if r := rt.rooms[rid]; r != nil {
		if c == r.clients[c.id] {
			if !c.registered() {
				log.Printf(">>> 确认客户端 %s 未注册，正在执行移除", c.id)
				rt.removeLocked(rid, c.id)
				return
			}
		}
	}
}

// wsCount 返回所有房间中已注册 WebSocket 的客户端总数。
func (rt *roomTable) wsCount() int {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	count := 0
	for _, r := range rt.rooms {
		count = count + r.wsCount()
	}
	return count
}
